package edu.hawaii.webspider;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.io.IOException;
import org.junit.Test;
import org.xml.sax.SAXException;
import com.meterware.httpunit.HttpNotFoundException;
import com.meterware.httpunit.WebResponse;
import com.meterware.pseudoserver.PseudoServer;

/**
 * JUnit tests for WebIterator.
 * 
 * @author Brian Jaress
 * 
 */
public class TestWebIterator {

  /**
   * Tests normal operation.
   */
  @Test
  public void testNormalIteration() {
    assertEquals("one page, no links",
        1, count(new int[][] {new int[] {}}));
    assertEquals("one page, link to self",
        1, count(new int[][] {new int[] {0}}));
    assertEquals("one page, linked to another",
        2, count(new int[][] {
        new int[] {1},
        new int[] {}
        }));
    assertEquals("two pages linked to each other",
        2, count(new int[][] {
        new int[] {1},
        new int[] {0}
        }));
    assertEquals("five pages linked in a circle",
        5, count(new int[][] {
        new int[] {1},
        new int[] {2},
        new int[] {3},
        new int[] {4},
        new int[] {0}
        }));
    assertEquals("four pages, each linked to the others",
        4, count(new int[][] {
        new int[] {1, 2, 3},
        new int[] {0, 2, 3},
        new int[] {0, 1, 3},
        new int[] {0, 1, 2},
        }));
    assertEquals("two pages, broken link",
        1, count(new int[][] {
            new int[] {2},
            new int[] {}
        }));
  }

  /**
   * Tests behavior on a missing start page.
   * 
   * @throws HttpNotFoundException always
   */
  @Test(expected = HttpNotFoundException.class)
  public void testMissingStartPage() {
    PseudoServer empty = new PseudoServer();
    try {
      (new WebIterator()).setStartPage(local(empty, "missing"));
    }
    catch (SAXException e) {
      fail(e.toString());
    }
    catch (IOException e) {
      fail(e.toString());
    }
  }

  /**
   * Test that non-HTML pages are handled correctly.
   */
  @Test
  public void testNonHtmlPage() {
    PseudoServer server = new PseudoServer();
    server.setResource("start",
        "<a href='binary'>binary</a><a href='html'>html</a>");
    server.setResource("binary", new byte[] { 0, 7 },
        "application/octet-stream");
    server.setResource("html", "");
    int pages = 0;
    WebIterator wi = new WebIterator();
    try {
      wi.setStartPage(local(server, "start"));
      for (@SuppressWarnings("unused")
      WebResponse response : wi) {
        pages++;
      }
    }
    catch (Exception e) {
      fail(e.toString());
    }
    assertEquals("non HTML page", 3, pages);
  }
  
  /**
   * Tests that remove throws an error.
   * 
   * @throws UnsupportedOperationException always
   */
  @Test(expected = UnsupportedOperationException.class)
  public void testRemove() {
    PseudoServer server = serverResources(new int[][] { new int[] {} });
    WebIterator wi = new WebIterator();
    try {
      wi.setStartPage(local(server, "0"));
    }
    catch (Exception e) {
      fail("could not get page to test removal");
    }
    wi.next();
    wi.remove();
  }

  /**
   * Test calling next past the end of the iteration.
   */
  @Test
  public void testPastEnd() {
    PseudoServer server = serverResources(new int[][] { new int[] {} });
    WebIterator wi = new WebIterator();
    try {
      wi.setStartPage(local(server, "0"));
    }
    catch (Exception e) {
      fail("could not get empty page");
    }
    wi.next();
    assertFalse("iteration should be over", wi.hasNext());
    assertNull("iteration should be over", wi.next());
  }
  
  /**
   * Creates a PseudoServer containing the specified
   * resources with links.
   * 
   * @param resources a 2D array specifying the resources.
   * If resources[i] = {j, k} then there will be a resource named
   * i containing links to resources j and k.
   * 
   * @return the server
   */
  public static PseudoServer serverResources(int[][] resources) {
    PseudoServer server = new PseudoServer();
    for (int i = 0; i < resources.length; i++) {
      StringBuilder content = new StringBuilder();
      for (int link : resources[i]) {
        content.append("<a href='" + link + "'>" + link + "</a>");
      }
      server.setResource(Integer.toString(i), content.toString());
    }
    return server;
  }

  /**
   * Create a server with the specified resources and iterate starting at resource 0.
   * 
   * @param resources resource spec,
   * as in {@link #serverResources(int[][])}
   * 
   * @return number of pages iterated over
   */
  private static int count(int[][] resources) {
    PseudoServer server = serverResources(resources);
    WebIterator wi = new WebIterator();
    try {
      wi.setStartPage(local(server, "0"));
    }
    catch (Exception e) {
      fail(e.toString());
    }
    int count = 0;
    for (@SuppressWarnings("unused")
    WebResponse response : wi) {
      count++;
    }
    return count;
  }

  /**
   * Constructs a local test path.
   * 
   * @param server a local PseudoServer
   * @param name local resource name
   * @return a path, including the appropriate port
   * @throws IOException on connection error
   */
  public static String local(PseudoServer server, String name)
      throws IOException {
    return "http://localhost:" + server.getConnectedPort() + "/" + name;
  }
}
